﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Reflection;
using System.IO;
using Littlefish.Validation;

namespace Littlefish.Model
{
    /// <summary>
    /// Class containing all the methods of loading and retrieving external code values.
    /// </summary>
    public class Codes
    {
        #region - Constructors -
        internal Codes(string codeList)
        {
            this.m_codeList = codeList;
        }
        #endregion

        #region - Private Fields -
        private string m_codeList;
        #endregion

        /// <summary>
        /// Number of codes in the list.
        /// </summary>
        public int Count
        {
            get { return GetCodes().Count; }
        }

        #region - Public Methods -
        /// <summary>
        /// Returns the description of the specified code.
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public string GetDescription(string code)
        {
            IDictionary<string, string> codes = GetCodes();
            if (codes.ContainsKey(code.Trim().ToUpperInvariant()))
                return codes[code.Trim().ToUpperInvariant()];
            else
                return string.Empty;
        }

        /// <summary>
        /// Determines whether the specified code exists.
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public bool CodeExists(string code)
        {
            IDictionary<string, string> codes = GetCodes();
            return codes.ContainsKey(code.Trim().ToUpperInvariant());
        }

        /// <summary>
        /// Gets all codes from the specified code list.
        /// </summary>
        /// <param name="codeList">The code list to retrieve values from</param>
        /// <returns></returns>
        public IDictionary<string, string> GetCodes()
        {
            ErrorHandler.SetError();
            Dictionary<string, string> codes = new Dictionary<string, string>();
            try
            {
                XElement lst = ExternalCodes.Element(m_codeList);
                if (lst != null)
                {
                    foreach (XElement x in lst.Elements())
                    {
                        try
                        {
                            codes.Add(x.Attribute("code").Value.ToUpperInvariant(), x.Value);
                        }
                        catch (ArgumentNullException ex)
                        {
                            ErrorHandler.SetError(ex);
                        }
                        catch (ArgumentException)
                        {
                            //Duplicate key found.
                            ErrorHandler.SetError(string.Format("{0} is a duplicate key.", x.Attribute("code")));
                        }
                        
                    }
                }
            }
            catch (NullReferenceException ex)
            {
                ErrorHandler.SetError(new ApplicationException(string.Format("Element {0} could not be found in Codes.xml", m_codeList), ex));
            }
            return codes;
        }
        #endregion

        #region - Static Properties -
        static XElement _externalCodes;
        /// <summary>
        /// Contains all of the external code definitions.
        /// </summary>
        static XElement ExternalCodes
        {
            get
            {
                if (_externalCodes == null)
                {
                    ErrorHandler.SetError();
                    //TODO: Replace Codes.xml which currently is a resource file, to an external file.
                    _externalCodes = XElement.Parse(Littlefish.Core.Properties.Resources.Codes);
                    if (_externalCodes == null)
                        ErrorHandler.SetError(new ApplicationException("Could not load the Codes.xml file."));
                }
                return _externalCodes;
            }
        }

        static ErrorHandler _errorHandler;
        /// <summary>
        /// Handles errors that occur in the <see cref="ExternalCodes"/> class;
        /// </summary>
        public static ErrorHandler ErrorHandler
        {
            get
            {
                //TODO: Add functionality to read in error handling values from config file.
                if (_errorHandler == null)
                    _errorHandler = new ErrorHandler(true);

                return _errorHandler;
            }

        }
        #endregion
    }

    /// <summary>
    /// List of external codes defined in Codes.xml
    /// </summary>
    public static class ExternalCodes
    {
        //TODO: Perhaps find a better structure for reading in the External Codes list.
        static ExternalCodes() { }

        static Codes _bithdateVerification = new Codes("BirthdateVerification");
        /// <summary>
        /// Birthdate verification codes as specififed by NCES0315BirthdateVerificationType.
        /// </summary>
        public static Codes BirthdateVerification
        {
            get { return _bithdateVerification; }
        }

        static Codes _countryCodes = new Codes("CountryCodes");
        /// <summary>
        /// 
        /// </summary>
        public static Codes CountryCodes
        {
            get { return _countryCodes; }
            set { _countryCodes = value; }
        }

        static Codes _dwellingArrangements = new Codes("DwellingArrangement");
        /// <summary>
        /// Dwelling Arrangement codes as specified by NCES0600DwellingArrangementType.
        /// <see cref="http://specification.sifinfo.org/Implementation/2.2/ExternalCodeSets.html#NCES0600DwellingArrangementType"/>
        /// </summary>
        public static Codes DwellingArrangements
        {
            get { return _dwellingArrangements; }
        }

        static Codes _genders = new Codes("Gender");
        /// <summary>
        /// 
        /// </summary>
        public static Codes Genders
        {
            get { return _genders; }
        }

        static Codes _languages = new Codes("LanguageCode");
        /// <summary>
        /// Language codes as specified by NISOZ3953LanguageCodesType.
        /// <see cref="http://specification.sifinfo.org/Implementation/2.2/ExternalCodeSets.html#NISOZ3953LanguageCodesType"/>
        /// </summary>
        public static Codes Languages
        {
            get { return _languages; }
        }

        static Codes _races = new Codes("Race");
        /// <summary>
        /// Race codes as specified by NCES0849RaceType.
        /// <see cref="http://specification.sifinfo.org/Implementation/2.2/ExternalCodeSets.html#NCES0849RaceType"/>
        /// </summary>
        public static Codes Races
        {
            get { return _races; }
        }

        static Codes _uspsAbbreviations = new Codes("USPSAbbreviations");
        /// <summary>
        /// 
        /// </summary>
        public static Codes USPSAbbreviations
        {
            get { return _uspsAbbreviations; }
        }

        static Codes _canadianProvinces = new Codes("CanadianProvincesAndTerritories");
        /// <summary>
        /// 
        /// </summary>
        public static Codes CanadianProvinces
        {
            get { return _canadianProvinces; }
        }

        static Codes _citizenshipStatus = new Codes("CitizenshipStatus");
        /// <summary>
        /// Race codes as specified by NCES0322CitizenshipStatusType.
        /// </summary>
        public static Codes CitizenshipStatus
        {
            get { return _citizenshipStatus; }
        }

        static Codes _englishProficiency = new Codes("EnglishProficiency");
        /// <summary>
        /// 
        /// </summary>
        public static Codes EnglishProficiency
        {
            get { return _englishProficiency; }
        }

        static Codes _martialStatus = new Codes("MaritalStatus");
        /// <summary>
        /// Marital status codes as specified by NCES0330MaritalStatusType.
        /// </summary>
        public static Codes MaritalStatus
        {
            get { return _martialStatus; }
        }
    }
}
